<?php
/**
 * Created by PhpStorm.
 * User: qch
 * Date: 2015/7/11
 * Time: 17:00
 */

namespace J\Storage;

use J\Exception;
use J\ICache;
use J\IPersister;

/**
 * 同步存储器，用于数据读取、模板文件编译
 *
 */
class Synchronizer extends Combine implements IPersister
{
    const PREPARE   = 0;
    const CONVERTER = 1;
    const COLUMNS   = 2;

    //use Combine;

    /**
     * @var IPersister
     */
    protected $persister;

    /**
     * @var ICache 缓存
     */
    protected $cache = null;

    /**
     * @var \Closure
     */
    protected $convert_value = null;

    /**
     * @var \Closure
     */
    protected $convert_key = null;

    /**
     * @var null|[]
     */
    protected $columns   = null;

    /**
     * Synchronizer constructor.
     *
     * @param IPersister|string $persister
     * @param ICache|string $cache
     * @param \Closure $convert_value 数据处理    源数据 => 存储格式
     * @param \Closure $convert_key 键名翻译器  存储键 => 缓存键
     * @param mixed $columns 参数
     *
     * @throws Exception
     */
    public function __construct($persister,
                                $cache,
                                \Closure $convert_value = null,
                                \Closure $convert_key = null,
                                $columns = null
    ){
        $this->convert_value   = $convert_value;
        $this->convert_key = $convert_key;
        $this->columns   = $columns;

        $this->persister = app()->getObject($persister);
        $this->cache     = app()->getObject($cache);
    }

    public function add($value)
    {
        $key = $this->persister->add($value);

        if($key and $this->cache) $this->cacheSet($key, $value);

        return $key;
    }

    public function getMulti($keys, $column = null)
    {
        if (!$this->cache) return $this->persister->getMulti($keys, $column);

        // 下面这段代码是防止cache和persister不同步
        $r = [];
        foreach ($keys as $key)
        {
            $r[$key] = $this->get($key, $column);
        }
        return $r;
    }

    public function get($key, $column = null)
    {
        if ($column === false or !$this->cache) return $this->persister->get($key, $column);

        return $this->getCache($key, $column);
    }

    /**
     * 同Get但get没有使用$dfg参数， 主要用于模板解析
     *
     * @param string $key 索引名
     * @param array $column 解析参数|读取列
     * @return mixed
     * @throws Exception
     */
    protected function getCache($key, $column = null)
    {
        $cache_key = $this->convertKey($key);

        if (
            !($this->cache->hasItem($cache_key, $this->persister->getExpire($key)))
        ){
            // 注意这里，没有使用column参数
            $value = $this->persister->get($key);

            $this->cacheSet($key, $value);
        }

        return $this->cache->get($cache_key, $column);
    }

    public function set($pairs, $keys = null)
    {
        $this->persister->set($pairs, $keys);

        if (!$this->cache) return;

        if ($keys === null) $keys = array_keys($pairs);

        $values = $this->persister->getMulti($keys);

        if(!$values) return;

        foreach ($values as $key => $value){
            $this->cache->set([
                $this->convertKey($key) =>
                    $this->convertValue($value, $key)
            ]);
        }
    }

    public function delete($keys)
    {
        $this->persister->delete($keys);
        if ($this->cache) $this->cache->delete($this->convertKey($keys));

        return $keys;
    }

    /**
     * 键转换
     * @param string|int $key
     * @return string|int
     */
    protected function convertKey($key)
    {
        if (!$this->convert_key) return $key;
        return call_user_func($this->convert_key, $key);
    }

    /**
     * 值转换
     * @param mixed $value
     * @param string|int $key
     * @return mixed 转换结果
     */
    protected function convertValue($value, $key = null)
    {
        if(!$this->convert_value) return $value;
        return call_user_func($this->convert_value, $value, $key);
    }

    /**
     * 设置缓存
     * @param string|int $key
     * @param $value
     * @throws Exception
     */
    protected function cacheSet($key, $value)
    {
        $this->cache->set([
            $this->convertKey($key) =>
                $this->convertValue($value, $key)
        ]);
    }
}